﻿using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Threading;
using System.Globalization;
using System.IO;

namespace ResourceTranslator
{
    public static class SatelliteAssemblyUtil
    {
        public static void CreateAssembly1(string assemblyFile, string[] resourceFiles, string culture)
        {
            CodeDomProvider cdp = CSharpCodeProvider.CreateProvider("cs");

            CompilerParameters cp = new CompilerParameters();

            if (cdp.Supports(GeneratorSupport.Resources))
            {
                foreach (string resourceFile in resourceFiles)
                {
                    cp.EmbeddedResources.Add(resourceFile);
                }
            }
            //cp.CompilerOptions = " /c:" + culture+" ";
            cp.OutputAssembly = assemblyFile;

            CompilerResults results = cdp.CompileAssemblyFromSource(cp, "");
            if (results.Errors.HasErrors)
            {
                throw new Exception("Error creating assembly: " + Environment.NewLine +
                    GetError(results.Errors));
            }
        }

        static string GetError(CompilerErrorCollection errors)
        {
            StringBuilder sb = new StringBuilder();
            foreach (CompilerError error in errors)
            {
                if (error.IsWarning)
                    continue;
                sb.AppendLine(error.ErrorText);
            }
            return sb.ToString();
        }

        public static void CreateAssembly(string assemblyFile, string[] resourceFiles, Version version, string culture, string keyfile)
        {
            if (resourceFiles == null || resourceFiles.Length == 0)
                return;

            bool workingOnTempDir = false;
            string workingDir;
            if (!ResourceDirsValid(resourceFiles, out workingDir))
            {
                workingOnTempDir = true;
                DirectoryInfo tempDirInfo = new DirectoryInfo(Path.GetTempPath());
                DirectoryInfo workingDirInfo = tempDirInfo.CreateSubdirectory(Guid.NewGuid().ToString());
                workingDir = workingDirInfo.FullName;

                foreach (string resFile in resourceFiles)
                {
                    string fileName = Path.GetFileName(resFile);
                    File.Copy(resFile, Path.Combine(workingDir, fileName));
                }
            }

            try
            {
                string assemblyFileName = Path.GetFileName(assemblyFile);
                string myAsmName = Path.GetFileNameWithoutExtension(assemblyFileName);// "WriteSatelliteAssembly.resources";

                AppDomain appDomain = Thread.GetDomain();//.CreateDomain("satelliteassemblycreator");

                AssemblyName asmName = new AssemblyName();
                asmName.Name = myAsmName;
                //asmName.CodeBase = assembliesDir;

                if (!String.IsNullOrEmpty(culture))
                    asmName.CultureInfo = new CultureInfo(culture);
                if (!String.IsNullOrEmpty(keyfile))
                    asmName.KeyPair = GetKeyPair(keyfile);
                //if (version != null)
                asmName.Version = version;

                AssemblyBuilder asmBuilder = appDomain.DefineDynamicAssembly(
                    asmName, AssemblyBuilderAccess.Save, workingDir);

                ModuleBuilder moduleBuilder = asmBuilder.DefineDynamicModule(myAsmName, assemblyFileName);

                foreach (string resourceFile in resourceFiles)
                {
                    string resourceFileName = Path.GetFileName(resourceFile);
                    asmBuilder.AddResourceFile(resourceFileName, resourceFileName, ResourceAttributes.Public);
                }

                asmBuilder.Save(assemblyFileName);

                if (!String.Equals(workingDir, Path.GetDirectoryName(assemblyFile)))
                {
                    File.Copy(Path.Combine(workingDir, assemblyFileName), assemblyFile, true);
                }
            }
            finally
            {
                if (workingOnTempDir)
                {
                    try
                    {
                        Directory.Delete(workingDir, true);
                    }
                    catch { }
                }
            }
        }

        static bool ResourceDirsValid(string[] resourceFiles, out string resourcesDir)
        {
            string dir = null;
            resourcesDir = null;
            foreach (string resourceFile in resourceFiles)
            {
                string thisResDir = Path.GetDirectoryName(resourceFile);
                if (dir == null)
                    dir = thisResDir;
                else
                {
                    if (!String.Equals(dir, thisResDir, StringComparison.CurrentCultureIgnoreCase))
                        return false;
                }
            }
            resourcesDir = dir;
            return true;
        }

        static StrongNameKeyPair GetKeyPair(string keyfile)
        {
            StrongNameKeyPair keypair;

            using (FileStream keyfileStream = File.OpenRead(keyfile))
                keypair = new StrongNameKeyPair(keyfileStream);

            return keypair;
        }
    }
}
